//+------------------------------------------------------------------+
//|                                           MoneySizeOptimized.mqh |
//|                             Copyright 2000-2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#include <Expert/ExpertMoney.h>       // 专家顾问资金管理基类（提供账户、品种、基础计算能力）
#include <Trade/DealInfo.h>           // 交易订单信息类（用于读取历史交易记录）

// wizard description start（MQL5 策略向导描述，用于识别类信息与参数）
//+------------------------------------------------------------------+
//| 类功能描述                                                       |
//| Title=手数优化型交易（连亏时动态降低手数）                        |
//| Type=Money                                                       |
//| Name=SizeOptimized                                                |
//| Class=CMoneySizeOptimized                                         |
//| Page=                                                            |
//| Parameter=DecreaseFactor,double,3.0,手数降低系数（连亏时手数衰减速度） |
//| Parameter=Percent,double,10.0,保证金/风险占用百分比（基础手数计算依据）|
//+------------------------------------------------------------------+
// wizard description end

//+------------------------------------------------------------------+
//| CMoneySizeOptimized 类：手数优化型资金管理实现                     |
//| 核心能力：继承自 CExpertMoney，结合“基础手数计算”与“连亏动态调整”，   |
//|           先按保证金/风险比例计算基础开仓手数，再根据历史交易连亏次数  |
//|           按系数降低手数，减少连续亏损时的资金回撤，优化风险收益比     |
//| 核心逻辑：                                                        |
//| 1. 基础手数 = 按账户资金比例计算的最大允许手数（同固定保证金策略）；    |
//| 2. 优化手数 = 基础手数 × (1 - 连亏次数/降低系数)（连亏≥2次时生效）；    |
//| 3. 最终手数 = 适配品种最小/最大手数及步长的优化手数                    |
//| 适用场景：追求稳健的交易策略（如趋势跟踪、震荡策略），尤其适合历史存在  |
//|           连续亏损周期的策略，通过手数动态调整平滑资金曲线              |
//+------------------------------------------------------------------+
class CMoneySizeOptimized : public CExpertMoney
  {
protected:
   // 手数优化核心参数（保护，子类可访问）
   double            m_decrease_factor;    // 手数降低系数（值越大，连亏时手数下降越慢；需>0，默认3.0）

public:
   //----------------------------------------------------------------
   // 构造与析构函数
   //----------------------------------------------------------------
   /**
    * @brief 构造函数：初始化手数优化型资金管理对象
    * @details 继承父类 CExpertMoney 初始化逻辑，设置手数降低系数默认值（3.0），
    *          自动关联账户（m_account）、交易品种（m_symbol）及历史订单读取能力
    */
                     CMoneySizeOptimized(void);

   /**
    * @brief 析构函数：清理手数优化型资金管理对象
    * @details 依赖父类析构逻辑释放账户、品种、历史订单等关联资源
    */
                    ~CMoneySizeOptimized(void);

   //----------------------------------------------------------------
   // 优化参数设置：修改手数降低系数
   //----------------------------------------------------------------
   /**
    * @brief 设置手数降低系数（控制连亏时手数衰减速度）
    * @param decrease_factor [in] 新系数值（必须>0，建议范围1.5-5.0）
    * @note 系数含义：
    *       - 系数=3.0（默认）：连亏2次，手数降低 2/3 ≈ 66.7%；连亏3次，手数降低 3/3=100%（降至最小手数）；
    *       - 系数越大，手数下降越平缓（如系数=5.0，连亏2次仅降低40%）；
    *       - 修改后建议调用 ValidationSettings() 验证有效性
    */
   void              DecreaseFactor(double decrease_factor);

   //----------------------------------------------------------------
   // 参数验证：确保基础参数与优化参数均有效
   //----------------------------------------------------------------
   /**
    * @brief 验证资金管理设置有效性（基础参数+优化参数）
    * @return bool - 验证结果：true=所有参数有效，false=存在无效参数（如系数≤0）
    * @note 验证逻辑：
    *       1. 先调用父类 ValidationSettings() 验证账户、品种、风险比例等基础参数；
    *       2. 额外验证手数降低系数（m_decrease_factor）是否>0，若无效则输出错误日志并返回false；
    *       3. 仅所有参数通过验证，策略才可正常计算开仓手数
    */
   virtual bool      ValidationSettings(void);

   //----------------------------------------------------------------
   // 开仓手数计算：核心方法（基础手数+连亏优化）
   //----------------------------------------------------------------
   /**
    * @brief 计算多单（买入）开仓手数（基础手数+连亏优化）
    * @param price [in] 计划开仓价格（若为0，自动使用当前市场卖价 m_symbol.Ask()）
    * @param sl [in] 止损价格（父类基础手数计算可能用到，优化逻辑不直接依赖）
    * @return double - 最终多单开仓手数（基础手数经连亏优化后，适配品种规则）
    * @note 计算流程：
    *       1. 调用父类账户对象 m_account.MaxLotCheck() 计算基础手数（按风险比例）；
    *       2. 调用 Optimize() 方法，根据历史连亏次数优化手数；
    *       3. 返回适配品种最小/最大手数及步长的最终手数
    */
   virtual double    CheckOpenLong(double price,double sl);

   /**
    * @brief 计算空单（卖出）开仓手数（基础手数+连亏优化）
    * @param price [in] 计划开仓价格（若为0，自动使用当前市场买价 m_symbol.Bid()）
    * @param sl [in] 止损价格（父类基础手数计算可能用到，优化逻辑不直接依赖）
    * @return double - 最终空单开仓手数（基础手数经连亏优化后，适配品种规则）
    * @note 计算流程与多单一致，仅交易方向（ORDER_TYPE_SELL）和市场价格（Bid价）不同
    */
   virtual double    CheckOpenShort(double price,double sl);

protected:
   //----------------------------------------------------------------
   // 手数优化核心方法：根据历史连亏次数调整基础手数（保护，仅内部/子类调用）
   //----------------------------------------------------------------
   /**
    * @brief 手数优化逻辑（核心私有方法，不可外部调用）
    * @param lots [in] 基础开仓手数（按风险比例计算的原始手数）
    * @return double - 优化后的手数（已适配品种最小/最大手数及步长）
    * @note 优化流程：
    *       1. 读取历史交易记录，从最新订单向前遍历，统计当前品种的连续亏损次数（中途遇盈利则停止）；
    *       2. 若连亏次数≥2，按公式计算优化手数：优化手数 = 基础手数 - 基础手数×连亏次数/降低系数；
    *       3. 按品种手数步长（LotsStep）取整，确保手数符合交易规则；
    *       4. 限制优化手数在品种最小（LotsMin）和最大（LotsMax）手数之间；
    *       5. 若连亏次数≤1，直接返回基础手数（无需优化）
    */
   double            Optimize(double lots);
  };
//+------------------------------------------------------------------+